package com.emotion.recognition.server.network;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.emotion.recognition.server.mlp.MLP;
import com.emotion.recognition.server.som.FeatureMap;
import com.emotion.recognition.server.som.SOM;
import com.emotion.recognition.shared.Constants;
import com.emotion.recognition.shared.Emotion;
import com.emotion.recognition.shared.MLPConfig;
import com.emotion.recognition.shared.Results;
import com.emotion.recognition.shared.SOMConfig;
import com.emotion.recognition.shared.SomResults;
import com.google.common.collect.Lists;

public class NeuralNetwork {

    public static Results runNeuralNetworks(List<List<Double>> training,
            List<List<Double>> targets, List<List<Double>> testing, MLPConfig mlp, SOMConfig som) {

        Results results = new Results();
        results.mlpResults = runMlp(training, targets, testing, mlp);
        results.somResults = runSom(training, targets, testing, som);

        return results;
    }

    private static Map<String, Double[]> runMlp(List<List<Double>> samples,
            List<List<Double>> targets, List<List<Double>> testing, MLPConfig mlp) {
        MLP net = MLP.builder().setInputLayer(400, -1.0d)
                .addHiddenLayer(mlp.hiddenLayerNodes, -1.0d).setOutputLayer(4)
                .setLearningRate(mlp.learningRate).setMomentumRate(mlp.momentumRate)
                .setMaxEpochs(mlp.maxEpochs).setDesiredMaxError(mlp.desiredMaxError)
                .buildClassifier();

        net.train(samples, targets);

        net.debugPrint();

        Map<String, Double[]> results = new HashMap<String, Double[]>();
        File folder = new File(Constants.TESTING_DIRECTORY);
        File[] testingFiles = folder.listFiles();
        Arrays.sort(testingFiles);

        for (int i = 0; i < testingFiles.length; ++i) {
            List<Double> output = net.sim(testing.get(i));
            System.out.println("DEBUG: " + testingFiles[i].getName() + " " + output.get(3) + " "
                    + output.get(2) + " " + output.get(1) + " " + output.get(0));
            Double[] a = new Double[4];
            a[0] = output.get(0);
            a[1] = output.get(1);
            a[2] = output.get(2);
            a[3] = output.get(3);
            results.put(testingFiles[i].getName(), a);
        }

        return results;
    }

    private static SomResults runSom(List<List<Double>> training, List<List<Double>> targets,
            List<List<Double>> testing, SOMConfig som) {
        SOM net = SOM.builder().setInputSize(400).setLatticeSize(som.latticeSize)
                .setRadius(som.radius).setLearningRate(som.learningRate)
                .setMaxEpochs(som.maxEpochs).buildClassifier();

        List<String> stringTargets = Lists.newArrayList();
        for (List<Double> target : targets) {
            stringTargets.add(Emotion.parseOneHotEncoded(target).toString());
        }

        net.classify(training, stringTargets);

        File folder = new File(Constants.TESTING_DIRECTORY);
        File[] testingFiles = folder.listFiles();
        Arrays.sort(testingFiles);

        SomResults somResults = new SomResults();
        Map<String, Map<Emotion, Double>> testingResults = new HashMap<String, Map<Emotion, Double>>();

        for (int i = 0; i < testingFiles.length; ++i) {
            testingResults.put(testingFiles[i].getName(),
                    net.testOneInput(testing.get(i), testingFiles[i].getName()));
        }

        FeatureMap featureMap = net.getFeatureMap();
        int size = featureMap.getMapSize();
        @SuppressWarnings("unchecked")
        // This is bad I think. But TODO for later.
        Map<String, Integer>[][] totalResults = new Map[size][size];
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                totalResults[i][j] = featureMap.nodes[i][j].getValues();
            }
        }

        somResults.somTotalResults = totalResults;
        somResults.somTestingResults = testingResults;

        return somResults;
    }
}
